class_name CharacterController
extends CharacterBody2D


#region 角色的配置属性

#跳跃动画帧最大索引
const JUMP_ANM_FRAME_MAX_INDEX = 4


#移动速度
const SPEED = 130.0

#最大跳跃速度
const JUMP_VELOCITY = -400.0


#最大的combo值
const MAX_COMBO: int = 3

#连招动画完毕后可以继续连招的时间
const COMBO_ANM_OVER_HOLD_TIME: float = 1.5

#连招后摇持续时间
const COMBO_ANM_OVER_SHAKE_TIME: float = 0.25

#endregion



#region 事件属性


#当前是否在地面上
var cur_on_ground: bool = false

#更新当前是否在地面状态
func setCurOnGround(newValue: bool) -> void:
	if cur_on_ground != newValue:
		if cur_on_ground:
			leaf_ground_sig.emit()
		else:
			touch_ground_sig.emit()
		cur_on_ground = newValue

#触地事件
signal touch_ground_sig()

#离地事件
signal leaf_ground_sig()


#当前角色的面朝方向 1-右边，-1-左边,由于角色的默认面朝为右边，所以赋值为1
var cur_face_dir: int = 1

#是否正在转向
var turnning_face: bool = false

#更新角色当前朝向
func setCurFaceDir(newValue: int) -> void:
	if cur_face_dir != newValue:
		face_change_sig.emit(newValue)
		cur_face_dir = newValue

#朝向更改的事件		
signal face_change_sig(newFaceDir)


#endregion


#region 组件引用

#角色的动画控制组件
@onready var character_anm_ctrl: CharacterAnmCtrl = $AnimatedSprite/AnimationPlayer/AnimationTree

#角色状态机控制器
@onready var stateMachine: StateMachine = $StateMechine

#角色的动画精灵
var character_anm_sprite: AnimatedSprite2D

#endregion

#region 生命周期


func _ready() -> void:
	#初始化触地状态
	update_character_on_ground()
	#初始化节点的属性
	init_nodes_property()
	add_move_about_listeners()
	add_jump_about_listeners()
	add_about_on_ground_listeners()
	add_character_turn_face_listeners()
	add_combo_listeners()
	

func _process(delta: float) -> void:
	update_character_on_ground()
	update_character_combo_step(delta)
	character_post_rock_timer_update(delta)

func _physics_process(delta: float) -> void:
	#重力模拟
	gravity_simulation(delta)
	#用于移动角色并沿碰撞面滑动，它会根据提供的速度向量移动角色，并在遇到碰撞时"滑动"沿着碰撞表面继续移动，而不是完全停止。
	move_and_slide()


func _exit_tree():
	remove_move_about_listeners()
	remove_jump_about_listeners()
	remove_about_on_ground_listeners()
	remove_character_turn_face_listeners()
	remove_combo_listeners()


#endregion

#初始化当前节点的相关属性
func init_nodes_property() -> void:
	character_anm_sprite = get_node("./AnimatedSprite")
	if character_anm_sprite != null:
		# print("character_anm_sprite 节点初始化成功")
		pass


#region 移动相关


#玩家的横向输入
var player_cur_horizontal_input: int = 0

#添加移动相关的监听器
func add_move_about_listeners() -> void:
	InputManager.move_input_sig.connect(character_move_listen)
	InputManager.move_stop_sig.connect(character_stop_move_listen)

#移除移动相关的监听器
func remove_move_about_listeners() -> void:
	InputManager.move_input_sig.disconnect(character_move_listen)
	InputManager.move_stop_sig.disconnect(character_stop_move_listen)

#添加角色调整面朝方向的监听
func add_character_turn_face_listeners() -> void:
	face_change_sig.connect(character_turn_face)

#移除角色调整面朝方向的监听
func remove_character_turn_face_listeners() -> void:
	face_change_sig.disconnect(character_turn_face)


#检测角色是否能够转向
func check_can_turn_face() -> bool:
	if comboing:
		return false
	if combo_over_shake_timer > 0:
		return false
	return true

#检测角色是否可以移动
func check_can_move() -> bool:
	if combo_over_shake_timer > 0:
		return false
	return true


#角色移动监听
func character_move_listen(delta, inputValue):
	#如果角色能够转向
	if check_can_turn_face():
		setCurFaceDir(1 if inputValue > 0 else -1)
	#如果角色不能够移动
	if not check_can_move():
		return
	player_cur_horizontal_input = inputValue
	#如果当前时闲置状态
	if stateMachine.curState.get_type() == StateMachine.CharacterStates.IDEL:
		#切换到移动状态
		stateMachine.switch_character_state(StateMachine.CharacterStates.RUN)
	

#角色停止监听
func character_stop_move_listen():
	player_cur_horizontal_input = 0
	if stateMachine.curState.get_type() == StateMachine.CharacterStates.RUN:
		#切换到闲置状态
		stateMachine.switch_character_state(StateMachine.CharacterStates.IDEL)

	
#角色调整面朝方向
func character_turn_face(faceDir: int) -> void:
	turnning_face = true
	# print("角色更改了朝向！现在面朝：", "右边" if faceDir > 0 else "左边")
	character_anm_sprite.flip_h = faceDir < 0
	turnning_face = false


#角色是否在地面上
func character_on_ground() -> bool:
	return is_on_floor()

#角色当前的速度
func character_cur_velocity() -> Vector2:
	return velocity


#玩家横向移动
func character_horizontal_move(deltaTime: float, rate: float) -> void:
	var newSpeed = player_cur_horizontal_input * SPEED * rate
	velocity.x = newSpeed

#endregion


#region 跳跃相关方法


#添加跳跃相关的监听器
func add_jump_about_listeners() -> void:
	InputManager.jump_sig.connect(character_jump_listen)
	InputManager.not_jump_sig.connect(character_no_jump_listen)

#移除跳跃相关监听
func remove_jump_about_listeners() -> void:
	InputManager.jump_sig.disconnect(character_jump_listen)
	InputManager.not_jump_sig.disconnect(character_no_jump_listen)


#添加触地、离地监听
func add_about_on_ground_listeners() -> void:
	touch_ground_sig.connect(on_character_touch_ground)
	leaf_ground_sig.connect(on_character_leaf_ground)

#移除触地、离地监听
func remove_about_on_ground_listeners() -> void:
	touch_ground_sig.disconnect(on_character_touch_ground)
	leaf_ground_sig.disconnect(on_character_leaf_ground)

#检测是能够跳跃
func check_can_jump() -> bool:
	#如果角色处于连招状态
	if stateMachine.curState.get_type() == StateMachine.CharacterStates.COMBO:
		return false
	#既没有在地面上，并且速度也不为空
	if not character_on_ground() || character_cur_velocity().y != 0:
		return false
	return true


#角色跳跃监听
func character_jump_listen() -> bool:
	if not check_can_jump():
		return false
	#切换到跳跃状态
	var switchRes = stateMachine.switch_character_state(StateMachine.CharacterStates.JUMP)
	if switchRes:
		velocity.y = JUMP_VELOCITY
	return switchRes

#角色不跳跃的监听
func character_no_jump_listen() -> void:
	pass


#角色触地事件
func on_character_touch_ground() -> void:
	#print("角色触地了")
	pass


#角色离地事件
func on_character_leaf_ground() -> void:
	#print("角色离地了")
	pass

#更新角色在地面的状态
func update_character_on_ground():
	setCurOnGround(is_on_floor())


#角色跳跃速度比率
func character_jump_speed_rate() -> float:
	return (character_cur_velocity().y / JUMP_VELOCITY) * JUMP_ANM_FRAME_MAX_INDEX

#endregion


#region 连招相关方法


#当前的combo值
var cur_combo_step: int = 0

#是否处于combo中
var comboing: bool = false


var combo_over_hold_timer: float = 0

var combo_over_shake_timer: float = 0

#添加连招相关监听
func add_combo_listeners() -> void:
	InputManager.combo_sig.connect(character_combo_listen)
	InputManager.not_combo_sig.connect(character_no_combo_listen)

#移除连招相关监听
func remove_combo_listeners() -> void:
	InputManager.combo_sig.disconnect(character_combo_listen)
	InputManager.not_combo_sig.disconnect(character_no_combo_listen)


#检测是否可以进行连招
func check_can_combo() -> bool:
	if comboing:
		return false
	return true

#角色连招监听
func character_combo_listen() -> bool:
	if not check_can_combo():
		return false
	if stateMachine.curState.get_type() == StateMachine.CharacterStates.COMBO || stateMachine.curState.get_type() == StateMachine.CharacterStates.POST_ROCK:
		return false
	return stateMachine.switch_character_state(StateMachine.CharacterStates.COMBO)

#角色不连招的监听
func character_no_combo_listen() -> void:
	pass

#更新角色的连招数
func update_character_combo_step(delta: float) -> void:
	if combo_over_hold_timer > 0:
		combo_over_hold_timer -= delta
		if combo_over_hold_timer <= 0:
			#重置连招数
			cur_combo_step = 0



#获取角色当前的连招状态
func character_cur_combo_step() -> int:
	return cur_combo_step

#endregion


#region 物理模拟
#重力模拟
func gravity_simulation(delta: float) -> void:
	#如果没有在地面上
	if not character_on_ground():
		velocity += get_gravity() * delta

#玩家开始连招
func character_combo_start() -> void:
	comboing = true

#玩家连招结束
func character_combo_over() -> void:
	comboing = false
	cur_combo_step += 1
	if cur_combo_step >= MAX_COMBO:
		cur_combo_step = 0
	#更新连招持续时间
	combo_over_hold_timer = COMBO_ANM_OVER_HOLD_TIME
	#更新连招的后摇时间
	combo_over_shake_timer = COMBO_ANM_OVER_SHAKE_TIME


#更新玩家的攻击后摇剩余时间
func character_post_rock_timer_update(delta: float) -> void:
	combo_over_shake_timer -= delta
	if combo_over_shake_timer < 0:
		combo_over_shake_timer = 0

#获取角色当前攻击后摇剩余时间
func character_combo_post_rock_timer() -> float:
	return combo_over_shake_timer

#endregion





#region 动画播放相关

#根据一个参数进行动画播放
func play_character_anm_one_param(anmName: String, value: float) -> void:
	if character_anm_ctrl:
		character_anm_ctrl.update_anm_one_param(anmName, value)

#播放角色动画
func play_character_anm(anm: String) -> void:
	if character_anm_ctrl:
		character_anm_ctrl.play_by_anmation_name(anm)

#判断当前动画是否为第一次播放完毕
func cur_character_anm_first_over() -> bool:
	if character_anm_ctrl:
		return character_anm_ctrl.get_cur_anm_play_rate() > 0.98
	return true


#endregion




#region 更改角色属性

#重置角色的速度
func reset_character_velocity() -> void:
	velocity = Vector2.ZERO

#重置角色x轴上的速度
func reset_character_x_velocity() -> void:
	velocity.x = 0

#endregion
